package com.linkgie.galaxyframework.utils;

import java.util.concurrent.Callable;

public class ExecutionScheduler {

    /**
     * 从指定的执行上下文开始定义执行上下文的调度策略；
     * <p>
     * 
     * @param context 要调度的执行上下文；
     * @return 返回上下文调度器；
     */
    public static ContextSchedule scheduleContext(ExecutionContext context) {
        return new ExecutionContextScheduler(context);
    }

    /**
     * 创建执行上下文；
     * @param contextHandle
     * @return
     */
    public static <T> ExecutionContext buildContext(ExecutionContextHandle<T> contextHandle){
        return new ExecutionContextHolder<T>(contextHandle);
    }

    private static class ExecutionContextScheduler implements ContextSchedule {

        private ExecutionContext scheduledContext;

        ExecutionContextScheduler(ExecutionContext context) {
            this.scheduledContext = context;
        }

        @Override
        public ContextSchedule enter(ExecutionContext innerContext) {
            ExecutionContextDelegater delegater = new ExecutionContextDelegater(this.scheduledContext, innerContext);
            return new ExecutionContextScheduler(delegater);
        }

        @Override
        public ContextSchedule wrap(ExecutionContext outerContext) {
            ExecutionContextDelegater delegater = new ExecutionContextDelegater(outerContext, this.scheduledContext);
            return new ExecutionContextScheduler(delegater);
        }

        @Override
        public ExecutionContext complete() {
            return scheduledContext;
        }

    }

    private static class ExecutionContextDelegater implements ExecutionContext {

        private ExecutionContext outer;

        private ExecutionContext inner;

        ExecutionContextDelegater(ExecutionContext outer, ExecutionContext inner) {
            this.outer = outer;
            this.inner = inner;
        }

        @Override
        public void run(Runnable runnable) throws RethrowException {
            this.outer.run(() -> inner.run(runnable));
        }

        @Override
        public <T> T call(Callable<T> callable) throws RethrowException {
            return this.outer.call(() -> inner.call(callable));
        }

    }

    private static class ExecutionContextHolder<S> implements ExecutionContext {

        private ExecutionContextHandle<S> handle;

        ExecutionContextHolder(ExecutionContextHandle<S> handle) {
            this.handle = handle;
        }

        @Override
        public void run(Runnable runnable) throws ExecutionException , ContextException{
            S context;
            try {
                context = handle.init();
            } catch (Exception e) {
                throw new ContextException(e.getMessage(), e);
            }
            Exception executionException = null;
            try {
                runnable.run();
            } catch (Exception e) {
                executionException = e;
                throw new ExecutionException(e.getMessage(), e);
            } finally {
                try {
                    handle.clean(context);
                } catch (Exception e) {
                    throw new RethrowException(executionException, e.getMessage(), e);
                }
            }
            
        }

    @Override
    public <T> T call(Callable<T> callable) throws ExecutionException, ContextException {
        S context;
        try {
            context = handle.init();
        } catch (Exception e) {
            throw new ContextException(e.getMessage(), e);
        }
        Exception executionException = null;
        try {
            return callable.call();
        } catch (Exception e) {
            executionException = e;
                throw new ExecutionException(e.getMessage(), e);
        } finally {
            try {
                handle.clean(context);
            } catch (Exception e) {
                throw new RethrowException(executionException, e.getMessage(), e);
            }
        }
    }

}}
